﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.Composition;
using System.IO;
using System.Diagnostics.CodeAnalysis;
using AutoMapper;

namespace Steve.EFDataLayer
{
    [Export(typeof(ContentAdministrationRepository))]
    [PartCreationPolicy(CreationPolicy.Shared)]
    public sealed class EFContentAdministrationRepository : ContentAdministrationRepository
    {
        static EFContentAdministrationRepository()
        {
            SteveEFDataLayer.SetupAutoMapper();
        }

        protected override Content SaveContentCore(Content toSave)
        {
            using (var ctx = new SteveEntities())
            {
                var existing = (from u in ctx.DbContents
                                where u.Id == toSave.Id
                                select u).FirstOrDefault();

                if (existing == null)
                {
                    existing = new DbContent();
                    ctx.AddToDbContents(existing);
                }

                Mapper.Map(toSave, existing);

                ctx.SaveChanges();

                return toSave;
            }
        }

        protected override void NewContentDataCore(Guid id, DateTime createDate, Stream data)
        {
            if (data == null) throw new ArgumentNullException("data");

            using (var ctx = new SteveEntities())
            {
                var db = new DbContentData()
                {
                    CreateDate = createDate,
                    Id = id
                };

                var buffer = new byte[16 * 1024];
                using (var ms = new MemoryStream())
                {
                    int read;
                    while ((read = data.Read(buffer, 0, buffer.Length)) > 0)
                    {
                        ms.Write(buffer, 0, read);
                    }
                    db.BinaryData = ms.ToArray();
                }

                ctx.AddToDbContentDatas(db);
                ctx.SaveChanges();
            }
        }

        protected override void MarkContentReadCore(Guid contentId, DateTime readDate, decimal? rating, Guid membershipId)
        {
            using (var ctx = new SteveEntities())
            {
                var query = from uc in ctx.DbUserContents
                            where uc.ContentId == contentId && uc.MembershipId == membershipId
                            select uc;

                var db = query.FirstOrDefault();
                var inDb = true;

                if (db == null)
                {
                    inDb = false;

                    db = new DbUserContent()
                    {
                        ContentId = contentId,
                        MembershipId = membershipId,
                    };

                    ctx.AddToDbUserContents(db);
                }

                if (rating != null)
                {
                    db.Rating = rating;
                    db.RatingDate = readDate;
                }
                else if (inDb)
                {
                    return;
                }

                ctx.SaveChanges();
            }
        }

        [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
        [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase")]
        protected override void SaveContentCategoriesCore(Guid contentId, IEnumerable<string> categories, DateTime revisionDate, Guid? revisionMembershipId)
        {
            if (categories == null)
                categories = new string[] { };
            else
                categories = categories.Select(name => name.ToLowerInvariant());

            var unmatchedCategories = from nc in categories
                                      join ec in CategoryRepository.GetCategories() on nc equals ec.CategoryName into existing
                                      where existing.FirstOrDefault() == null
                                      select nc;

            if (unmatchedCategories.Any())
            {
                throw new ForeignKeyException(unmatchedCategories.ToArray());
            }

            using (var ctx = new SteveEntities())
            {
                var existing = (from cc in ctx.DbContentCategories
                                where cc.ContentId == contentId
                                select cc).ToList();

                var toDelete = from cc in existing
                               where !categories.Contains(cc.CategoryName)
                               select cc;

                var toInsert = from name in categories
                               join cc in existing on name equals cc.CategoryName.ToLowerInvariant() into existingGroup
                               let cc = existingGroup.FirstOrDefault()
                               where cc == null || cc.DeleteDate != null
                               select new { Name = name, Existing = cc };

                foreach (var cc in toDelete)
                {
                    cc.DeleteDate = revisionDate;
                    cc.DeleteMembershipId = revisionMembershipId;
                }

                foreach (var cc in toInsert)
                {
                    if (cc.Existing != null)
                    {
                        cc.Existing.DeleteDate = null;
                        cc.Existing.DeleteMembershipId = null;
                    }
                    else
                    {
                        var newValue = new DbContentCategory();

                        newValue.ContentId = contentId;
                        newValue.CategoryName = cc.Name;
                        newValue.CreateDate = revisionDate;
                        newValue.CreateMembershipId = revisionMembershipId;

                        ctx.AddToDbContentCategories(newValue);
                    }
                }

                ctx.SaveChanges();
            }
        }

        protected override void ApproveContentCore(Guid contentId, int revisionNumber)
        {
            using (var ctx = new SteveEntities())
            {
                ctx.ApproveContent(contentId, revisionNumber, CurrentAuthenticatedMembershipId, CurrentRevisionDate);
            }
        }

        public override void RebuildContentSimilarityCache(Guid? updatedId)
        {
            using (var ctx = new SteveEntities())
            {
                ctx.RebuildContentSimilarityCache(updatedId);
            }
        }

        
    }
}
